﻿using System;
using Rhino.Mocks;

namespace CleanTest
{
    internal abstract class TestConstructBase<TInput, TOuptut> : ITestConstruct<TInput, TOuptut>
    {
        protected Func<TInput, TOuptut> _functionUnderTest;
        protected TInput _input;
        protected bool hasAssertion;

        public ITestConstruct<TInput, TOuptut> WhenICall(Func<TInput, TOuptut> functionUnderTest)
        {
            _functionUnderTest = functionUnderTest;
            return this;
        }

        public ITestConstruct<TInput, TOuptut> With(TInput input)
        {
            _input = input;
            return this;
        }

        public void ExpectThatICall<TDependency>(TDependency mock, Action<TDependency> action)
            where TDependency : class
        {
            if (hasAssertion)
                throw new InvalidTestSetupException("You cannot set up more than one assertion/verification on a test");
            hasAssertion = true;
            Expectation<TDependency>.RunExpectation(mock, action, () => _functionUnderTest(_input));
        }

        public void ExpectThatICall<TDependency, TDependencyOutput>(TDependency mock, Function<TDependency, TDependencyOutput> expectedFunction, TDependencyOutput dependencyOutput) where TDependency : class
        {
            if (hasAssertion)
                throw new InvalidTestSetupException("You cannot set up more than one assertion/verification on a test");
            hasAssertion = true;
            Expectation<TDependency>.RunExpectation(mock, expectedFunction, dependencyOutput, () => _functionUnderTest(_input));
        }

        public void ExpectThatINeverCall<TDependency>(TDependency mock, Action<TDependency> action) where TDependency : class
        {
            if (hasAssertion)
                throw new InvalidTestSetupException("You cannot set up more than one assertion/verification on a test");
            hasAssertion = true;
            Expectation<TDependency>.RunNeverExpectation(mock, action, () => _functionUnderTest(_input));
        }

        public abstract void AssertOutputIsSatifiedBy(Func<TOuptut, bool> predicate);
        public abstract void AnExceptionIsExpected<T>() where T : Exception;
    }
}
